#pragma once
#include <iostream>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
#include "sem.hpp"

const int g_DFL_CAPACITY = 5;

template<class T>
class RingQueue 
{
public:
    // 构造函数 
    RingQueue(int capacity = g_DFL_CAPACITY)
        : _ring_queue(capacity, T()) 
        , _capacity(capacity)
        , _data_sem(0)
        , _space_sem(capacity)
        , _step_consume(0)
        , _step_productor(0)
    { }

    // 析构函数
    ~RingQueue()
    { }

    // 放数据函数，由生产者调用
    void push(const T& val)
    {
        // 1.申请空间信号量p
        _space_sem.p();

        // 2.向环形队列中放置数据（访问临界资源step要加锁解锁）
        pthread_mutex_lock(&_mtx_prod);
        _ring_queue[_step_productor++] = val;
        _step_productor %= _capacity;    // 下一个生产者生产数据的位置
        pthread_mutex_unlock(&_mtx_prod);

        // 3.发布数据信号量
        _data_sem.v();
    }

    // 取数据函数，由发布者调用
    void pop(T* val)
    {
        // 1. 申请数据信号量
        _data_sem.p();

        // 2.从环形队列中拿数据（访问临界资源step要加锁解锁）
        pthread_mutex_lock(&_mtx_con);
        *val = _ring_queue[_step_consume++];
        _step_consume %= _capacity;
        pthread_mutex_unlock(&_mtx_con);

        // 3.发布空间信号量
        _space_sem.v();
    }

private:
    std::vector<T> _ring_queue; // 用数组模拟环形队列
    int _capacity;              // 环形队列最大容量
    Sem _data_sem;              // 队列中数据信号量
    Sem _space_sem;             // 剩余空间信号量
    pthread_mutex_t _mtx_prod;  // 生产者锁
    pthread_mutex_t _mtx_con;   // 消费者锁
    int _step_consume;          // 消费者拿数据的下标
    int _step_productor;        // 生产者写数据的下标
};